﻿namespace MicroCloud.Authentication.OAuth2.TikTok
{
    /// <summary>
    /// 抖音认证处理器
    /// </summary>
    /// <remarks>
    /// 初始化一个抖音认证处理器 <see cref="TikTokHandler"/> 的新实例
    /// </remarks>
    /// <param name="options"></param>
    /// <param name="logger"></param>
    /// <param name="encoder"></param>
    internal class TikTokHandler(IOptionsMonitor<TikTokOptions> options, ILoggerFactory logger, UrlEncoder encoder) : OAuthHandler<TikTokOptions>(options, logger, encoder)
    {
        #region "受保护的重写方法 - Overrides of OAuthHandler<QQOptions>"
        #region "Step 1：请求CODE，构建用户授权地址"
        /// <summary>
        /// Step 1：请求CODE，构建用户授权地址
        /// </summary>
        /// <param name="properties"></param>
        /// <param name="redirectUri"></param>
        /// <returns></returns>
        protected override string BuildChallengeUrl(AuthenticationProperties properties, string redirectUri)
        {
            return QueryHelpers.AddQueryString(Options.AuthorizationEndpoint, new Dictionary<string, string>
            {
                ["client_key"] = Options.ClientId,
                ["response_type"] = "code",
                ["scope"] = FormatScope(),
                ["redirect_uri"] = redirectUri,
                ["state"] = Options.StateDataFormat.Protect(properties)
            });
        }
        #endregion
        #region "Step 2：通过code获取access_token"
        /// <summary>
        /// Step 2：通过code获取access_token
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        protected override async Task<OAuthTokenResponse> ExchangeCodeAsync(OAuthCodeExchangeContext context)
        {
            string code = context.Code;
            // string redirectUri = context.RedirectUri;

            var address = QueryHelpers.AddQueryString(Options.TokenEndpoint, new Dictionary<string, string>()
            {
                ["client_key"] = Options.ClientId,
                ["client_secret"] = Options.ClientSecret,
                ["code"] = code,
                ["grant_type"] = "authorization_code",
            });

            var response = await Backchannel.GetAsync(address);
            string responseContentJson = await response.Content.ReadAsStringAsync();
            if (!response.IsSuccessStatusCode)
            {
                Logger.LogError("检索访问令牌时出错：远程服务器返回了具有以下负载的{Status}响应：{Headers}{Body}.",
                                /* Status: */ response.StatusCode,
                                /* Headers: */ response.Headers.ToString(),
                                /* Body: */ responseContentJson);

                return OAuthTokenResponse.Failed(new System.Exception("检索访问令牌时出错"));
            }

            var payload = JObject.Parse(responseContentJson).Value<JObject>("data");
            JsonDocument document = JsonDocument.Parse(payload.ToJsonString());
            if (payload.Value<string>("error_code") != "0")
            {
                Logger.LogError("检索访问令牌时出错：远程服务器返回了具有以下负载的{Status}响应：{Headers}{Body}.",
                                /* Status: */ response.StatusCode,
                                /* Headers: */ response.Headers.ToString(),
                                /* Body: */ responseContentJson);

                return OAuthTokenResponse.Failed(new System.Exception("检索访问令牌时出错"));
            }

            return OAuthTokenResponse.Success(document);
        }
        #endregion
        #region "Step 3：从远程服务器创建票证"
        /// <summary>
        /// Step 3：从远程服务器创建票证
        /// </summary>
        /// <param name="identity"></param>
        /// <param name="properties"></param>
        /// <param name="tokens"></param>
        /// <returns></returns>
        protected override async Task<AuthenticationTicket> CreateTicketAsync(
            ClaimsIdentity identity,
            AuthenticationProperties properties,
            OAuthTokenResponse tokens)
        {
            tokens.Response.RootElement.TryGetProperty("openid", out JsonElement openIdElement);
            var openId = openIdElement.GetString();
            //var openId = JObject.Parse(tokens.Response.ToJsonString()).Value<string>("open_id");
            var address = QueryHelpers.AddQueryString(Options.UserInformationEndpoint, new Dictionary<string, string>
            {
                ["access_token"] = tokens.AccessToken,
                ["open_id"] = openId
            });

            var response = await Backchannel.GetAsync(address);
            string responseContentJson = await response.Content.ReadAsStringAsync();
            if (!response.IsSuccessStatusCode)
            {
                Logger.LogError("检索用户配置文件时出错：远程服务器返回了具有以下负载的{Status}响应：{Headers}{Body}.",
                                /* Status: */ response.StatusCode,
                                /* Headers: */ response.Headers.ToString(),
                                /* Body: */ responseContentJson);

                throw new HttpRequestException("检索用户信息时出错");
            }

            var payload = JObject.Parse(responseContentJson);
            JsonDocument document = JsonDocument.Parse(responseContentJson);
            if (payload.Value<string>("error_code") != "0")
            {
                Logger.LogError("检索用户配置文件时出错：远程服务器返回了具有以下负载的{Status}响应：{Headers}{Body}.",
                                /* Status: */ response.StatusCode,
                                /* Headers: */ response.Headers.ToString(),
                                /* Body: */ responseContentJson);

                throw new HttpRequestException("检索用户信息时出错");
            }
            //error_code Integer     optional
            //description String optional
            //open_id String  用户在当前应用的唯一标识 optional
            //union_id String  用户在当前开发者账号下的唯一标识（未绑定开发者账号没有该字段）	optional
            //nickname    String optional
            //avatar String      optional
            //city    String optional
            //province String      optional
            //country String optional
            //gender EnumGender  性别: `0` -未知 `1` -男性 * `2` -女性    optional
            //e_account_role  EnumEAccountRole 类型: `EAccountM` -普通企业号 `EAccountS` -认证企业号 * `EAccountK` -品牌企业号
            var open_id = payload.Value<string>("open_id");
            var union_id = payload.Value<string>("union_id");
            union_id ??= open_id;
            var nickname = payload.Value<string>("nickname");
            var avatar = payload.Value<string>("avatar");

            identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, union_id, Options.ClaimsIssuer));
            identity.AddClaim(new Claim(ClaimTypes.Name, nickname, Options.ClaimsIssuer));
            identity.AddClaim(new Claim(ClaimTypeConstants.OpenIdEx, open_id, Options.ClaimsIssuer));
            identity.AddClaim(new Claim(ClaimTypeConstants.HeadImgEx, avatar, Options.ClaimsIssuer));

            var context = new OAuthCreatingTicketContext(new ClaimsPrincipal(identity), properties, Context, Scheme, Options, Backchannel, tokens, document.RootElement);
            context.RunClaimActions();

            await Events.CreatingTicket(context);

            return new AuthenticationTicket(context.Principal, context.Properties, Scheme.Name);
        }
        #endregion

        #endregion

        #region "私有方法"
        #region "格式化作用域"
        /// <summary>
        /// 格式化作用域
        /// </summary>
        /// <returns></returns>
        protected override string FormatScope()
        {
            return string.Join(",", Options.Scope);
        }
        #endregion

        #endregion

    }

}
